////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   material.cpp
//  Version:     v1.00
//  Created:     3/2/2003 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "Material.h"
#include "MaterialManager.h"
#include "BaseLibrary.h"
#include "ErrorReport.h"

#include <I3DEngine.h>
#include "IEntityRenderState.h"
#include <CryArray.h>
#include <CryHeaders.h>
#include <ICryAnimation.h>
#include <ISourceControl.h>

#include "../Console/ConsoleSync.h"

static bool defaultTexMod_Initialized = false;
static SEfTexModificator defaultTexMod;
static SInputShaderResources defaultShaderResource;

//////////////////////////////////////////////////////////////////////////
CMaterial::CMaterial( CMaterialManager *pManager,const CString &name,int nFlags )
{
	m_pManager = pManager;
	m_scFileAttributes = SCC_FILE_ATTRIBUTE_NORMAL;

	m_pParent = 0;

	if (!defaultTexMod_Initialized)
	{
		ZeroStruct(defaultTexMod);
		defaultTexMod.m_Tiling[0] = 1;
		defaultTexMod.m_Tiling[1] = 1;
		defaultTexMod_Initialized = true;
	
	}
	
	m_shaderResources = defaultShaderResource;
	m_shaderResources.m_Opacity = 1;  
	m_shaderResources.m_CloakAmount = 255;
	m_shaderResources.m_LMaterial.m_Diffuse.Set(1.0f,1.0f,1.0f,1.0f);
	m_shaderResources.m_LMaterial.m_SpecShininess = 10.0f;

	m_mtlFlags = nFlags;
	ZeroStruct(m_shaderItem);

	// Default shader.
	m_shaderName = "Illum";
	m_nShaderGenMask = 0;

	m_name = name;
	m_bRegetPublicParams = true;
	m_bKeepPublicParamsValues = false;
	m_bIgnoreNotifyChange = false;
	m_bDummyMaterial = false;
	m_bFromEngine = false;
	
	m_pHighlightData = 0;

	m_pMatInfo = NULL;
}

//////////////////////////////////////////////////////////////////////////
CMaterial::~CMaterial()
{
	if (m_pHighlightData)
	{
		delete m_pHighlightData;
		m_pHighlightData = 0;
	}

	if (IsModified())
		Save(false);

	// Release used shader.
	SAFE_RELEASE( m_shaderItem.m_pShader );
	SAFE_RELEASE( m_shaderItem.m_pShaderResources );

	if (m_pMatInfo)
	{
		m_pMatInfo->SetUserData(0);
		m_pMatInfo = 0;
	}
	if (!m_subMaterials.empty())
	{
		for (int i = 0; i < m_subMaterials.size(); i++)
			if (m_subMaterials[i])
				m_subMaterials[i]->m_pParent = NULL;
		m_subMaterials.clear();
	}

	if (!IsPureChild() && !(GetFlags() & MTL_FLAG_UIMATERIAL))
	{
		// Unregister this material from manager.
		// Don't use here local variable m_pManager. Manager can be destroyed.
		if(GetIEditor()->GetMaterialManager())
			GetIEditor()->GetMaterialManager()->DeleteItem(this);
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetName( const CString &name )
{
	if (name != m_name)
	{
		CString oldName = GetFullName();
		m_name = name;
		if (!IsPureChild())
		{
			m_pManager->OnRenameItem( this,oldName );
			if (m_pMatInfo)
			{
				GetIEditor()->Get3DEngine()->GetMaterialManager()->RenameMaterial( m_pMatInfo,GetName() );
			}
		}
		else
		{
			if (m_pMatInfo)
				m_pMatInfo->SetName(m_name);
		}
		NotifyChanged();
	}
	if (m_shaderItem.m_pShaderResources)
	{
		// Only for correct warning message purposes.
		m_shaderItem.m_pShaderResources->SetMaterialName(m_name);
	}
}

//////////////////////////////////////////////////////////////////////////
CString CMaterial::GetFilename(bool bForWriting)
{
	CString filename = GetMatManager()->MaterialToFilename(m_name, bForWriting);
	return filename;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::UpdateFileAttributes()
{
	if (GetFilename().IsEmpty())
		return;

	m_scFileAttributes = GetIEditor()->GetSourceControl()->GetFileAttributes(GetFilename());
}

//////////////////////////////////////////////////////////////////////////
uint32 CMaterial::GetFileAttributes( bool bUpdateFromFile )
{
	if (IsDummy())
		return m_scFileAttributes;

	if (IsPureChild() && m_pParent)
		return m_pParent->GetFileAttributes();

	if (bUpdateFromFile)
		UpdateFileAttributes();
	return m_scFileAttributes;
};

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetShaderName( const CString &shaderName )
{
	if (m_shaderName != shaderName)
	{
    // update params !

		m_bRegetPublicParams = true;
		m_bKeepPublicParamsValues = false;
		RecordUndo("Change Shader");
	}
	m_shaderName = shaderName;
	if (stricmp(m_shaderName,"nodraw") == 0)
		m_mtlFlags |= MTL_FLAG_NODRAW;
	else
		m_mtlFlags &= ~MTL_FLAG_NODRAW;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::CheckSpecialConditions()
{
	if (stricmp(m_shaderName,"nodraw") == 0)
		m_mtlFlags |= MTL_FLAG_NODRAW;
	else
		m_mtlFlags &= ~MTL_FLAG_NODRAW;

	// If environment texture name have auto/nearest cubemap in it, force material to use auto cube-map for it.
	if (!m_shaderResources.m_Textures[EFTT_ENV].m_Name.empty())
	{
		const char *sAtPos = strstr(m_shaderResources.m_Textures[EFTT_ENV].m_Name.c_str(),"auto_cubemap");
		if (sAtPos)
			m_shaderResources.m_Textures[EFTT_ENV].m_Sampler.m_eTexType = eTT_AutoCube;	// Force Auto-Cubemap
	}

	// Force nearest cube map if user sets texture type
	if( m_shaderResources.m_Textures[EFTT_ENV].m_Sampler.m_eTexType == eTT_NearestCube) 
	{
		if( m_shaderResources.m_Opacity <1.0f ) 
		{
			m_shaderResources.m_Textures[EFTT_ENV].m_Name = "nearest_cubemap";
			m_mtlFlags |= MTL_FLAG_REQUIRE_NEAREST_CUBEMAP;
		}
		else
		{
			// Set default
			m_shaderResources.m_Textures[EFTT_ENV].m_Name = "";
			m_shaderResources.m_Textures[EFTT_ENV].m_Sampler.m_eTexType = eTT_2D;
		}
	}

}

//////////////////////////////////////////////////////////////////////////
bool CMaterial::LoadShader()
{
	if (m_bDummyMaterial)
		return true;

	CheckSpecialConditions();

//	if (shaderName.IsEmpty())
//		return false;

	GetIEditor()->GetErrorReport()->SetCurrentValidatorItem( this );

	/*
	if (m_mtlFlags & MTL_FLAG_LIGHTING)
		m_shaderResources.m_LMaterial = &m_lightMaterial;
	else
		m_shaderResources.m_LMaterial = 0;
	*/

	m_shaderResources.m_ResFlags = m_mtlFlags;

	CString sShader = m_shaderName;
	if (sShader.IsEmpty())
	{
		sShader = "<Default>";
	}

  m_shaderResources.m_szMaterialName = m_name;
	SShaderItem newShaderItem = GetIEditor()->GetRenderer()->EF_LoadShaderItem( sShader,false,0,&m_shaderResources,m_nShaderGenMask );

  // Shader not found
	if (newShaderItem.m_pShader && (newShaderItem.m_pShader->GetFlags() & EF_NOTFOUND) != 0)
		CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING, "Failed to load shader \"%s\" in material \"%s\"",newShaderItem.m_pShader->GetName(), m_name);

  //if (newShaderItem.m_pShader)
  //{
  //  newShaderItem.m_pShader->AddRef();
  //}

  //if (newShaderItem.m_pShaderResources)
  //{
  //  newShaderItem.m_pShaderResources->AddRef();
  //}

  // Release previously used shader (Must be After new shader is loaded, for speed).
  SAFE_RELEASE( m_shaderItem.m_pShader );
  SAFE_RELEASE( m_shaderItem.m_pShaderResources );

	m_shaderItem = newShaderItem;

	if (!m_shaderItem.m_pShader)
	{
		CErrorRecord err;
		err.error.Format( _T("Failed to Load Shader %s"),(const char*)m_shaderName );
		err.pItem = this;
		GetIEditor()->GetErrorReport()->ReportError( err );
		GetIEditor()->GetErrorReport()->SetCurrentValidatorItem( NULL );
		return false;
	}

	IShader *pShader = m_shaderItem.m_pShader;
	m_nShaderGenMask = pShader->GetGenerationMask();
	if (pShader->GetFlags() & EF_NOPREVIEW)
		m_mtlFlags |= MTL_FLAG_NOPREVIEW;
	else
		m_mtlFlags &= ~MTL_FLAG_NOPREVIEW;

  CMaterial* pMtlTmpl = NULL;
  if( !m_matTemplate.IsEmpty() )
    pMtlTmpl = GetIEditor()->GetMaterialManager()->LoadMaterial(m_matTemplate,false); 

  //////////////////////////////////////////////////////////////////////////
	// Reget shader params.
	//////////////////////////////////////////////////////////////////////////
	if (m_bRegetPublicParams)
	{
		if (m_bKeepPublicParamsValues)
		{
			m_bKeepPublicParamsValues = false;
			m_publicVarsCache = XmlHelpers::CreateXmlNode( "PublicParams" );
			SetXmlFromShaderParams(m_shaderResources, m_publicVarsCache );
		}
		m_shaderResources.m_ShaderParams = pShader->GetPublicParams();
	}
	m_bRegetPublicParams = false;

	//////////////////////////////////////////////////////////////////////////
	// If we have XML node with public parameters loaded, apply it on shader params.
	//////////////////////////////////////////////////////////////////////////
	if (m_publicVarsCache)
	{
    SetShaderParamsFromXml(m_shaderResources, m_publicVarsCache);
		m_publicVarsCache = 0;
	}

	//////////////////////////////////////////////////////////////////////////
	// Set shader params.
	if (m_shaderItem.m_pShaderResources)
		m_shaderItem.m_pShaderResources->SetShaderParams( &m_shaderResources, m_shaderItem.m_pShader );
	//////////////////////////////////////////////////////////////////////////



  //////////////////////////////////////////////////////////////////////////
  // Set Shader Params for material layers
  //////////////////////////////////////////////////////////////////////////

	if (m_pMatInfo)
	{    
		UpdateMatInfo();
	}  

	GetIEditor()->GetErrorReport()->SetCurrentValidatorItem( NULL );
	return true;
}

bool CMaterial::LoadMaterialLayers()
{
  if (!m_pMatInfo)
  {
    return false;
  }

  if( m_shaderItem.m_pShader && m_shaderItem.m_pShaderResources )
  {
    // mask generation for base material shader
    uint64 nMaskGenBase = m_shaderItem.m_pShader->GetGenerationMask();  
    SShaderGen *pShaderGenBase = m_shaderItem.m_pShader->GetGenerationParams();

    for(uint32 l(0); l < MTL_LAYER_MAX_SLOTS; ++l)
    {
      SMaterialLayerResources *pCurrLayer = &m_pMtlLayerResources[l];        
      pCurrLayer->m_nFlags |= MTL_FLAG_NODRAW;
      if(!pCurrLayer->m_shaderName.IsEmpty())
      {
        if (strcmpi(pCurrLayer->m_shaderName,"nodraw") == 0)
        {          
          // no shader = skip layer
          pCurrLayer->m_shaderName.Empty();          
          continue;
        }

        IShader *pNewShader = GetIEditor()->GetRenderer()->EF_LoadShader( pCurrLayer->m_shaderName, 0);
        
        // Check if shader loaded
				if (!pNewShader || (pNewShader->GetFlags() & EF_NOTFOUND) != 0)
				{
					CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING, "Failed to load material layer shader \"%s\" in material \"%s\"", pCurrLayer->m_shaderName, m_pMatInfo->GetName());
        if( !pNewShader )
          continue;
        }
        
        if( !pCurrLayer->m_pMatLayer )
        {
          pCurrLayer->m_pMatLayer = m_pMatInfo->CreateLayer();
        }

        // mask generation for base material shader
        uint64 nMaskGenLayer = 0;  
        SShaderGen *pShaderGenLayer = pNewShader->GetGenerationParams();    
        if (pShaderGenBase && pShaderGenLayer)
        {                        
          for (int nLayerBit(0); nLayerBit < pShaderGenLayer->m_BitMask.size(); ++nLayerBit)
          {
            SShaderGenBit *pLayerBit = pShaderGenLayer->m_BitMask[nLayerBit];

            for (int nBaseBit(0); nBaseBit < pShaderGenBase->m_BitMask.size(); ++nBaseBit)
            {
              SShaderGenBit *pBaseBit = pShaderGenBase->m_BitMask[nBaseBit];
              
              // Need to check if flag name is common to both shaders (since flags values can be different), if so activate it on this layer
              if( nMaskGenBase&pBaseBit->m_Mask )
              {                
                if (!pLayerBit->m_ParamName.empty() && !pBaseBit->m_ParamName.empty())
                {
                  if( pLayerBit->m_ParamName ==  pBaseBit->m_ParamName )
                  {
                    nMaskGenLayer |= pLayerBit->m_Mask; 
                    break;
                  }
                }
              }

            }

          }
        }

        // Reload with proper flags
        SShaderItem newShaderItem = GetIEditor()->GetRenderer()->EF_LoadShaderItem( pCurrLayer->m_shaderName, false, 0, &pCurrLayer->m_shaderResources, nMaskGenLayer);        
				if (!newShaderItem.m_pShader || (newShaderItem.m_pShader->GetFlags() & EF_NOTFOUND) != 0)
				{
					CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_WARNING, "Failed to load material layer shader \"%s\" in material \"%s\"", pCurrLayer->m_shaderName, m_pMatInfo->GetName());
        if (!newShaderItem.m_pShader )
          continue;
        }

        SShaderItem &pCurrShaderItem = pCurrLayer->m_pMatLayer->GetShaderItem();

        if (newShaderItem.m_pShader)
        {
          newShaderItem.m_pShader->AddRef();
        }

        // Release previously used shader (Must be After new shader is loaded, for speed).
        SAFE_RELEASE( pCurrShaderItem.m_pShader ); 
        SAFE_RELEASE( pCurrShaderItem.m_pShaderResources );
        SAFE_RELEASE( newShaderItem.m_pShaderResources );

        pCurrShaderItem.m_pShader = newShaderItem.m_pShader;
        // Copy resources from base material
        pCurrShaderItem.m_pShaderResources = m_shaderItem.m_pShaderResources->Clone();
        pCurrShaderItem.m_nTechnique = newShaderItem.m_nTechnique;
        pCurrShaderItem.m_nPreprocessFlags = newShaderItem.m_nPreprocessFlags;

        // set default params
        if( pCurrLayer->m_bRegetPublicParams)
        {
          pCurrLayer->m_shaderResources.m_ShaderParams = pCurrShaderItem.m_pShader->GetPublicParams();        
        }

        pCurrLayer->m_bRegetPublicParams = false;

        if( pCurrLayer->m_publicVarsCache )
        {
          CMaterial::SetShaderParamsFromXml( pCurrLayer->m_shaderResources, pCurrLayer->m_publicVarsCache );
          pCurrLayer->m_publicVarsCache = 0;  
        }

        if (pCurrShaderItem.m_pShaderResources )
        {
          pCurrShaderItem.m_pShaderResources->SetShaderParams( &pCurrLayer->m_shaderResources, pCurrShaderItem.m_pShader );
        }

        // Activate layer
        pCurrLayer->m_nFlags &= ~MTL_FLAG_NODRAW;
      }

    }

    return true;
  }

  return false;
}

//////////////////////////////////////////////////////////////////////////

void CMaterial::UpdateMaterialLayers()
{
  if (m_pMatInfo && m_shaderItem.m_pShaderResources)
  {
    m_pMatInfo->SetLayerCount( MTL_LAYER_MAX_SLOTS );

    uint8 nMaterialLayerFlags = 0;

    for(int l(0); l< MTL_LAYER_MAX_SLOTS; ++l)
    {      
      SMaterialLayerResources *pCurrLayer = &m_pMtlLayerResources[l]; 
      if( pCurrLayer && !pCurrLayer->m_shaderName.IsEmpty() && pCurrLayer->m_pMatLayer)
      {                                
        pCurrLayer->m_pMatLayer->SetFlags( pCurrLayer->m_nFlags );
        m_pMatInfo->SetLayer(l, pCurrLayer->m_pMatLayer);        

        if( (pCurrLayer->m_nFlags & MTL_LAYER_USAGE_NODRAW) )
        {
          if( !strcmpi(pCurrLayer->m_shaderName, "frozenlayerwip")) 
            nMaterialLayerFlags |= MTL_LAYER_FROZEN ;
          else
          if( !strcmpi(pCurrLayer->m_shaderName, "cloaklayer")) 
            nMaterialLayerFlags |= MTL_LAYER_CLOAK;
      }
    }
  }

    if( m_shaderItem.m_pShaderResources )
      m_shaderItem.m_pShaderResources->SetMtlLayerNoDrawFlags( nMaterialLayerFlags );
}
}

void CMaterial::UpdateMatInfo()
{  
	if (m_pMatInfo)
	{
		// Mark material invalid.
		m_pMatInfo->SetFlags( m_mtlFlags );
		m_pMatInfo->SetShaderItem( m_shaderItem );
		m_pMatInfo->SetSurfaceType( m_surfaceType );
    m_pMatInfo->SetMatTemplate( m_matTemplate );

    LoadMaterialLayers();
    UpdateMaterialLayers();
    
		if (IsMultiSubMaterial())
		{
			m_pMatInfo->SetSubMtlCount(m_subMaterials.size());
			for (unsigned int i = 0; i < m_subMaterials.size(); i++)
			{
				if (m_subMaterials[i])
					m_pMatInfo->SetSubMtl( i,m_subMaterials[i]->GetMatInfo() );
				else
					m_pMatInfo->SetSubMtl( i,NULL );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CVarBlock* CMaterial::GetPublicVars( SInputShaderResources &pShaderResources )
{
	if (pShaderResources.m_ShaderParams.empty())
		return 0;

	CVarBlock *pPublicVars = new CVarBlock;
	for (int i = 0; i < pShaderResources.m_ShaderParams.size(); i++)
	{
		IVariable *pIVar = NULL;
		SShaderParam *pParam = &pShaderResources.m_ShaderParams[i];
		switch (pParam->m_Type)
		{
		case eType_BYTE:
			{
			CVariable<int> *pVar = new CVariable<int>;
			pVar->SetName( pParam->m_Name );
			*pVar = pParam->m_Value.m_Byte;
			pPublicVars->AddVariable( pVar );
			pIVar = pVar;
			}
			break;
		case eType_SHORT:
			{
			CVariable<int> *pVar = new CVariable<int>;
			*pVar = pParam->m_Value.m_Short;
			pVar->SetName( pParam->m_Name );
			pPublicVars->AddVariable( pVar );
			pIVar = pVar;
			}
			break;
		case eType_INT:
			{
			CVariable<int> *pVar = new CVariable<int>;
			*pVar = pParam->m_Value.m_Int;
			pVar->SetName( pParam->m_Name );
			pPublicVars->AddVariable( pVar );
			pIVar = pVar;
			}
			break;
		case eType_FLOAT:
			{
			CVariable<float> *pVar = new CVariable<float>;
			*pVar = pParam->m_Value.m_Float;      
			pVar->SetName( pParam->m_Name );
			pPublicVars->AddVariable( pVar );

			pIVar = pVar;
			}
			break;
	/*
		case eType_STRING:
			pVar = new CVariable<string>;
			*pVar = pParam->m_Value.m_String;
			pVar->SetName( pParam->m_Name.c_str() );
			pPublicVars->AddVariable( pVar );
			break;
			*/
		case eType_FCOLOR:
			{
			CVariable<Vec3> *pVar = new CVariable<Vec3>;
			*pVar = Vec3(pParam->m_Value.m_Color[0],pParam->m_Value.m_Color[1],pParam->m_Value.m_Color[2]);
			pVar->SetName( pParam->m_Name );
			pVar->SetDataType( IVariable::DT_COLOR );
			pPublicVars->AddVariable( pVar );
			pIVar = pVar;
			}
			break;
		case eType_VECTOR:
			{
			CVariable<Vec3> *pVar = new CVariable<Vec3>;
			*pVar = Vec3(pParam->m_Value.m_Vector[0],pParam->m_Value.m_Vector[1],pParam->m_Value.m_Vector[2]);
			pVar->SetName( pParam->m_Name );
			pPublicVars->AddVariable( pVar );      
			pIVar = pVar;
			}
			break;
		}
		if (pIVar && pParam->m_Script.size())
		{
			ParsePublicParamsScript( pParam->m_Script.c_str(),pIVar );
		}
	}

	return pPublicVars;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetPublicVars( CVarBlock *pPublicVars, SInputShaderResources &pInputShaderResources, IRenderShaderResources *pRenderShaderResources, IShader *pShader)
{
	assert( pPublicVars );

	if (pInputShaderResources.m_ShaderParams.empty())
		return;

	RecordUndo("Set Public Vars");

	int numVars = pPublicVars->GetVarsCount();

	for (int i = 0; i < numVars; i++)
	{
		if (i >= numVars)
			break;

		IVariable *pVar = pPublicVars->GetVariable(i);
		SShaderParam *pParam = NULL;
		for (int j = 0; j < pInputShaderResources.m_ShaderParams.size(); j++)
		{	
			if (strcmp(pVar->GetName(),pInputShaderResources.m_ShaderParams[j].m_Name) == 0)
			{
				pParam = &pInputShaderResources.m_ShaderParams[j];
				break;
			}
		}
		if (!pParam)
			continue;

		switch (pParam->m_Type)
		{
		case eType_BYTE:
			if (pVar->GetType() == IVariable::INT)
			{
				int val = 0;
				pVar->Get(val);
				pParam->m_Value.m_Byte = val;
			}
			break;
		case eType_SHORT:
			if (pVar->GetType() == IVariable::INT)
			{
				int val = 0;
				pVar->Get(val);
				pParam->m_Value.m_Short = val;
			}
			break;
		case eType_INT:
			if (pVar->GetType() == IVariable::INT)
			{
				int val = 0;
				pVar->Get(val);
				pParam->m_Value.m_Int = val;
			}
			break;
		case eType_FLOAT:
			if (pVar->GetType() == IVariable::FLOAT)
			{
				float val = 0;
				pVar->Get(val);
				pParam->m_Value.m_Float = val;
			}
			break;
			/*
		case eType_STRING:
			if (pVar->GetType() == IVariable::STRING)
			{
				CString str;
				int val = 0;
				pVar->Get(val);
				pParam->m_Value.m_Byte = val;
			}
			break;
			*/
		case eType_FCOLOR:
			if (pVar->GetType() == IVariable::VECTOR)
			{
				Vec3 val(0,0,0);
				pVar->Get(val);
				pParam->m_Value.m_Color[0] = val.x;
				pParam->m_Value.m_Color[1] = val.y;
				pParam->m_Value.m_Color[2] = val.z;
			}
			break;
		case eType_VECTOR:
			if (pVar->GetType() == IVariable::VECTOR)
			{
				Vec3 val(0,0,0);
				pVar->Get(val);
				pParam->m_Value.m_Vector[0] = val.x;
				pParam->m_Value.m_Vector[1] = val.y;
				pParam->m_Value.m_Vector[2] = val.z;
			}
			break;
		}
	}
  //////////////////////////////////////////////////////////////////////////
  // Set shader params.
  if (pRenderShaderResources)
    pRenderShaderResources->SetShaderParams( &pInputShaderResources, pShader );
  //////////////////////////////////////////////////////////////////////////
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::ParsePublicParamsScript( const char *sUIScript,IVariable *pVar )
{
	string uiscript = sUIScript;
	string element[3];
	int p1 = 0;
	string itemToken = uiscript.Tokenize( ";",p1 );
	while (!itemToken.empty())
	{
		int nElements = 0;
		int p2 = 0;
		string token = itemToken.Tokenize( " \t\r\n=",p2 );
		while (!token.empty())
		{
			element[nElements++] = token;
			if (nElements == 2)
			{
				element[nElements] = itemToken.substr( p2 );
				element[nElements].Trim( " =\t\"" );
				break;
			}
			token = itemToken.Tokenize( " \t\r\n=",p2 );
		};

		float minLimit,maxLimit;
		pVar->GetLimits( minLimit,maxLimit );

		if (stricmp(element[1],"UIWidget") == 0)
		{
			if (stricmp(element[2],"Color") == 0)
				pVar->SetDataType( IVariable::DT_COLOR );
		}
		else if (stricmp(element[1],"UIHelp") == 0)
		{
			string help = element[2];
			help.replace( "\\n","\n" );
			pVar->SetDescription( help );
		}
		else if (stricmp(element[1],"UIName") == 0)
		{
			pVar->SetHumanName( element[2].c_str() );
		}
		else if (stricmp(element[1],"UIMin") == 0)
		{
			pVar->SetLimits( atof(element[2]),maxLimit );

		}
		else if (stricmp(element[1],"UIMax") == 0)
		{
			pVar->SetLimits( minLimit,atof(element[2]) );
		}
		else if (stricmp(element[1],"UIStep") == 0)
		{
			
		}
		
		itemToken = uiscript.Tokenize(";",p1);

	};

}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetShaderParamsFromXml( SInputShaderResources &pShaderResources, XmlNodeRef &node )
{
	if (pShaderResources.m_ShaderParams.empty())
		return;
	
	int nValue;
	float fValue;
	Vec3 vValue;

	for (int i = 0; i < pShaderResources.m_ShaderParams.size(); i++)
	{
		SShaderParam *pParam = &pShaderResources.m_ShaderParams[i];
		switch (pParam->m_Type)
		{
		case eType_BYTE:
			if (node->getAttr(pParam->m_Name,nValue))
				pParam->m_Value.m_Byte = nValue;
			break;
		case eType_SHORT:
			if (node->getAttr(pParam->m_Name,nValue))
				pParam->m_Value.m_Short = nValue;
			break;
		case eType_INT:
			if (node->getAttr(pParam->m_Name,nValue))
				pParam->m_Value.m_Int = nValue;
			break;
		case eType_FLOAT:
			if (node->getAttr(pParam->m_Name,fValue))
				pParam->m_Value.m_Float = fValue;
			break;
		case eType_FCOLOR:
			if (node->getAttr(pParam->m_Name,vValue))
			{
				pParam->m_Value.m_Color[0] = vValue.x;
				pParam->m_Value.m_Color[1] = vValue.y;
				pParam->m_Value.m_Color[2] = vValue.z;
			}
			break;
		case eType_VECTOR:
			if (node->getAttr(pParam->m_Name,vValue))
			{
				pParam->m_Value.m_Vector[0] = vValue.x;
				pParam->m_Value.m_Vector[1] = vValue.y;
				pParam->m_Value.m_Vector[2] = vValue.z;
			}
			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetXmlFromShaderParams( SInputShaderResources &pShaderResources, XmlNodeRef &node )
{
	for (int i = 0; i < pShaderResources.m_ShaderParams.size(); i++)
	{
		SShaderParam *pParam = &pShaderResources.m_ShaderParams[i];
		switch (pParam->m_Type)
		{
		case eType_BYTE:
			node->setAttr(pParam->m_Name,(int)pParam->m_Value.m_Byte );
			break;
		case eType_SHORT:
			node->setAttr(pParam->m_Name,(int)pParam->m_Value.m_Short );
			break;
		case eType_INT:
			node->setAttr(pParam->m_Name,(int)pParam->m_Value.m_Int );
			break;
		case eType_FLOAT:
			node->setAttr(pParam->m_Name,(float)pParam->m_Value.m_Float );
			break;
		case eType_FCOLOR:
			node->setAttr(pParam->m_Name,Vec3(pParam->m_Value.m_Color[0],pParam->m_Value.m_Color[1],pParam->m_Value.m_Color[2]) );
			break;
		case eType_VECTOR:
			node->setAttr(pParam->m_Name,Vec3(pParam->m_Value.m_Vector[0],pParam->m_Value.m_Vector[1],pParam->m_Value.m_Vector[2]) );
			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CVarBlock* CMaterial::GetShaderGenParamsVars()
{	
	if (!m_shaderItem.m_pShader)
		return 0;
	IShader *pTemplShader = m_shaderItem.m_pShader;
	if (!pTemplShader)
		return 0;

	SShaderGen *pShaderGen = pTemplShader->GetGenerationParams();
	if (!pShaderGen)
		return 0;

	CVarBlock *pBlock = new CVarBlock;
	for (int i = 0; i < pShaderGen->m_BitMask.size(); i++)
	{
		SShaderGenBit *pGenBit = pShaderGen->m_BitMask[i];
		if (pGenBit->m_Flags & SHGF_HIDDEN)
			continue;
		if (!pGenBit->m_ParamProp.empty())
		{
			CVariable<bool> *pVar = new CVariable<bool>;
			pBlock->AddVariable( pVar );
			pVar->SetName( pGenBit->m_ParamProp.c_str() );
			*pVar = (pGenBit->m_Mask & m_nShaderGenMask) != 0;
			pVar->SetDescription( pGenBit->m_ParamDesc.c_str() );
		}
	}
/*
  // make sure if no valid generation parameters to not create new tab
  if(!pBlock->GetVarsCount())
  {
    SAFE_DELETE(pBlock);    
    return 0;
  }
*/
	return pBlock;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetShaderGenParamsVars( CVarBlock *pBlock )
{
	RecordUndo("Change Shader GenMask");

	if (!m_shaderItem.m_pShader)
		return;
	IShader *pTemplShader = m_shaderItem.m_pShader;
	if (!pTemplShader)
		return;

	SShaderGen *pShaderGen = pTemplShader->GetGenerationParams();
	if (!pShaderGen)
		return;

	uint64 nGenMask = 0;

	for (int i = 0; i < pShaderGen->m_BitMask.size(); i++)
	{
		SShaderGenBit *pGenBit = pShaderGen->m_BitMask[i];
		if (pGenBit->m_Flags & SHGF_HIDDEN)
			continue;

		if (!pGenBit->m_ParamProp.empty())
		{
			IVariable *pVar = pBlock->FindVariable(pGenBit->m_ParamProp.c_str());
			if (!pVar)
				continue;
			bool bFlagOn = false;
			pVar->Get(bFlagOn); 
			if (bFlagOn)
				nGenMask |= pGenBit->m_Mask;
		}
	}
	if (m_nShaderGenMask != nGenMask)
	{
		m_bRegetPublicParams = true;
		m_bKeepPublicParamsValues = true;
		m_nShaderGenMask = nGenMask;
	}
}

//////////////////////////////////////////////////////////////////////////
unsigned int CMaterial::GetTexmapUsageMask() const
{
	int mask = 0;
	if (m_shaderItem.m_pShader)
	{
		IShader *pTempl = m_shaderItem.m_pShader;
		if (pTempl)
			mask = pTempl->GetUsedTextureTypes();
	}
	return mask;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::Update()
{
	// Reload shader item with new resources and shader.
	LoadShader();

	// Mark library as modified.
	SetModified();

	GetIEditor()->SetModifiedFlag(); 

	// When modifying pure child, mark his parent as modified.
	if (IsPureChild() && m_pParent)
	{
		m_pParent->SetModified();
		m_pParent->SyncMaterialToConsole();
	}
	else
	{
		SyncMaterialToConsole();
	}
}

namespace
{
	inline Vec3 ToVec3( const ColorF &col ) { return Vec3(col.r,col.g,col.b); }
	inline ColorF ToCFColor( const Vec3 &col ) { return ColorF(col); }
};

static struct
{
	int texId;
	const char *name;
} sUsedTextures[] =
{
	{ EFTT_DIFFUSE,		"Diffuse" },
	{ EFTT_GLOSS,			"Specular" },
	{ EFTT_BUMP,			"Bumpmap" },
	{ EFTT_ENV,		"Environment" },
	{ EFTT_DETAIL_OVERLAY,"Detail" },
	{ EFTT_OPACITY,		"Opacity" },
	{ EFTT_DECAL_OVERLAY,	"Decal" },
	{ EFTT_SUBSURFACE,	"SubSurface" },
	{ EFTT_CUSTOM,	"Custom" },
	{ EFTT_CUSTOM_SECONDARY,	"[1] Custom" },
};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CMaterial::Serialize( SerializeContext &ctx )
{
	//CBaseLibraryItem::Serialize( ctx );

	XmlNodeRef node = ctx.node;
	if (ctx.bLoading)
	{
		m_bIgnoreNotifyChange = true;
		m_bRegetPublicParams = true;

		SInputShaderResources &sr = m_shaderResources;

		m_shaderResources = defaultShaderResource;

		// Loading
		int flags = m_mtlFlags;
		if (node->getAttr( "MtlFlags",flags ))
		{
			m_mtlFlags &= ~(MTL_FLAGS_SAVE_MASK);
			m_mtlFlags |= (flags & (MTL_FLAGS_SAVE_MASK));
		}

		CMaterial* pMtlTmpl=NULL;

		if (!IsMultiSubMaterial())
		{
			node->getAttr( "MatTemplate",m_matTemplate );
			//
			if( !m_matTemplate.IsEmpty() && m_matTemplate!=GetName() )
			{
				pMtlTmpl = GetIEditor()->GetMaterialManager()->LoadMaterial(m_matTemplate,false); 
				if( !pMtlTmpl )
				{
					m_matTemplate = "";
				}
			}
			else
			{
				m_matTemplate = "";
			}
			if( !pMtlTmpl )
			{
				node->getAttr( "Shader",m_shaderName );
				node->getAttr( "GenMask",m_nShaderGenMask );

				if( !( m_mtlFlags&MTL_64BIT_SHADERGENMASK) )
				{
					uint32 nShaderGenMask = 0;
					node->getAttr( "GenMask",nShaderGenMask );
					m_nShaderGenMask = nShaderGenMask;
				}
				else
					node->getAttr( "GenMask",m_nShaderGenMask );

				// Remap flags if needed
				if( !(m_mtlFlags & MTL_64BIT_SHADERGENMASK) )
				{
					m_nShaderGenMask = GetIEditor()->GetRenderer()->EF_GetRemapedShaderMaskGen( (const char*) m_shaderName, m_nShaderGenMask ); 
					m_mtlFlags |= MTL_64BIT_SHADERGENMASK;
				}

				if( node->getAttr( "StringGenMask",m_pszShaderGenMask ) )         
					m_nShaderGenMask = GetIEditor()->GetRenderer()->EF_GetShaderGlobalMaskGenFromString( (const char*) m_shaderName, (const char*)m_pszShaderGenMask, m_nShaderGenMask);  // get common mask gen
				else
				{
					// version doens't has string gen mask yet ? Remap flags if needed
					m_nShaderGenMask = GetIEditor()->GetRenderer()->EF_GetRemapedShaderMaskGen( (const char*) m_shaderName, m_nShaderGenMask, ((m_mtlFlags & MTL_64BIT_SHADERGENMASK)!=0) ); 
				}
				m_mtlFlags |= MTL_64BIT_SHADERGENMASK;

				node->getAttr( "SurfaceType",m_surfaceType );

				// Load lighting data.
				Vec3 vColor;
				if (node->getAttr( "Diffuse",vColor ))
					m_shaderResources.m_LMaterial.m_Diffuse = ToCFColor(vColor);
				if (node->getAttr( "Specular",vColor ))
					m_shaderResources.m_LMaterial.m_Specular = ToCFColor(vColor);
				if (node->getAttr( "Emissive",vColor ))
					m_shaderResources.m_LMaterial.m_Emission = ToCFColor(vColor);

				node->getAttr( "Shininess",m_shaderResources.m_LMaterial.m_SpecShininess );
				node->getAttr( "Opacity",sr.m_Opacity );
				node->getAttr( "AlphaTest",sr.m_AlphaRef );
				node->getAttr( "GlowAmount",sr.m_GlowAmount );
				node->getAttr( "FurAmount",sr.m_FurAmount );				

				if( node->getAttr( "HeatAmount", sr.m_HeatAmount ) )					
				{
					// keep backward compatibility with old mtls - rescale mtl heat range
					const float fHeatRange = MAX_HEATSCALE;
					float fHeatAmount = ((float)sr.m_HeatAmount) * (1.0f / 255.0f) / fHeatRange;
					sr.m_HeatAmount = int_round( fHeatAmount * 255.0f );
				}
				else
					node->getAttr( "HeatAmountScaled", sr.m_HeatAmount );

				node->getAttr( "CloakAmount",sr.m_CloakAmount );

				int vertModif = sr.m_DeformInfo.m_eType;
				if (node->getAttr( "vertModifType", vertModif ))
					sr.m_DeformInfo.m_eType = (EDeformType)vertModif;
			}
			else
			{
				m_mtlFlags = (m_mtlFlags & ~MTL_FLAGS_TEMPLATE_MASK) | (pMtlTmpl->m_mtlFlags & MTL_FLAGS_TEMPLATE_MASK);
				m_shaderName = pMtlTmpl->m_shaderName;
				m_nShaderGenMask = pMtlTmpl->m_nShaderGenMask;
				m_surfaceType = pMtlTmpl->m_surfaceType;
				m_shaderResources = pMtlTmpl->m_shaderResources;
			}
			//
			CString texmap;
			// 
			XmlNodeRef texturesNode = node->findChild( "Textures" );
			if (texturesNode)
			{
				for (int i = 0; i < texturesNode->getChildCount(); i++)
				{
					texmap = "";
					XmlNodeRef texNode = texturesNode->getChild(i);
					texNode->getAttr( "Map",texmap );

					int texId = -1;
					for (int j = 0; j < sizeof(sUsedTextures)/sizeof(sUsedTextures[0]); j++)
					{
						if (stricmp(sUsedTextures[j].name,texmap) == 0)
						{
							texId = sUsedTextures[j].texId;
							break;
						}
					}
					if (texId < 0)
						continue;

					const char *filename = texNode->getAttr( "File" );

					// Correct texid found.
					sr.m_Textures[texId].m_Name = filename;

					if( pMtlTmpl )
					{
						sr.m_Textures[texId].m_bUTile = pMtlTmpl->m_shaderResources.m_Textures[texId].m_bUTile;
						sr.m_Textures[texId].m_bVTile = pMtlTmpl->m_shaderResources.m_Textures[texId].m_bVTile;
						sr.m_Textures[texId].m_Sampler.m_eTexType = pMtlTmpl->m_shaderResources.m_Textures[texId].m_Sampler.m_eTexType;
						sr.m_Textures[texId].m_Filter = pMtlTmpl->m_shaderResources.m_Textures[texId].m_Filter;
						sr.m_Textures[texId].m_Ext = pMtlTmpl->m_shaderResources.m_Textures[texId].m_Ext;
					}
					else
					{
						texNode->getAttr( "IsTileU",sr.m_Textures[texId].m_bUTile );
						texNode->getAttr( "IsTileV",sr.m_Textures[texId].m_bVTile );
						texNode->getAttr( "TexType",sr.m_Textures[texId].m_Sampler.m_eTexType );
						//
						int filter = sr.m_Textures[texId].m_Filter;
						if (texNode->getAttr( "Filter",filter ))
							sr.m_Textures[texId].m_Filter = filter;
						//
						XmlNodeRef modNode = texNode->findChild( "TexMod" );
						if (modNode)
						{
							SEfTexModificator& texm = *sr.m_Textures[texId].AddModificator();

							// Modificators
							modNode->getAttr( "TileU",texm.m_Tiling[0] );
							modNode->getAttr( "TileV",texm.m_Tiling[1] );
							modNode->getAttr( "OffsetU",texm.m_Offs[0] );
							modNode->getAttr( "OffsetV",texm.m_Offs[1] );

							float f;
							modNode->getAttr( "TexMod_bTexGenProjected",texm.m_bTexGenProjected );
							modNode->getAttr( "TexMod_UOscillatorType",texm.m_eUMoveType );
							modNode->getAttr( "TexMod_VOscillatorType",texm.m_eVMoveType );
							modNode->getAttr( "TexMod_RotateType",texm.m_eRotType );
							modNode->getAttr( "TexMod_TexGenType",texm.m_eTGType );

							if (modNode->getAttr( "RotateU",f ))
								texm.m_Rot[0] = Degr2Word(f);
							if (modNode->getAttr( "RotateV",f ))
								texm.m_Rot[1] = Degr2Word(f);
							if (modNode->getAttr( "RotateW",f ))
								texm.m_Rot[2] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_URotateRate",f ))
								texm.m_RotOscRate[0] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_VRotateRate",f ))
								texm.m_RotOscRate[1] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_WRotateRate",f ))
								texm.m_RotOscRate[2] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_URotatePhase",f ))
								texm.m_RotOscPhase[0] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_VRotatePhase",f ))
								texm.m_RotOscPhase[1] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_WRotatePhase",f ))
								texm.m_RotOscPhase[2] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_URotateAmplitude",f ))
								texm.m_RotOscAmplitude[0] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_VRotateAmplitude",f ))
								texm.m_RotOscAmplitude[1] = Degr2Word(f);
							if (modNode->getAttr( "TexMod_WRotateAmplitude",f ))
								texm.m_RotOscAmplitude[2] = Degr2Word(f);
							modNode->getAttr( "TexMod_URotateCenter",texm.m_RotOscCenter[0] );
							modNode->getAttr( "TexMod_VRotateCenter",texm.m_RotOscCenter[1] );
							modNode->getAttr( "TexMod_WRotateCenter",texm.m_RotOscCenter[2] );

							modNode->getAttr( "TexMod_UOscillatorRate",texm.m_UOscRate );
							modNode->getAttr( "TexMod_VOscillatorRate",texm.m_VOscRate );
							modNode->getAttr( "TexMod_UOscillatorPhase",texm.m_UOscPhase );
							modNode->getAttr( "TexMod_VOscillatorPhase",texm.m_VOscPhase );
							modNode->getAttr( "TexMod_UOscillatorAmplitude",texm.m_UOscAmplitude );
							modNode->getAttr( "TexMod_VOscillatorAmplitude",texm.m_VOscAmplitude );
						}
					}
				}
			}
		}
		if( !pMtlTmpl )
		{
			//////////////////////////////////////////////////////////////////////////
			// Check if we have vertex deform.
			//////////////////////////////////////////////////////////////////////////
			XmlNodeRef deformNode = node->findChild("VertexDeform");
			if (deformNode)
			{
				int type = eDT_Unknown;
				deformNode->getAttr( "Type",type );
				m_shaderResources.m_DeformInfo.m_eType = (EDeformType)type;
				deformNode->getAttr( "DividerX",m_shaderResources.m_DeformInfo.m_fDividerX );
				deformNode->getAttr( "DividerY",m_shaderResources.m_DeformInfo.m_fDividerY );
				deformNode->getAttr( "DividerZ",m_shaderResources.m_DeformInfo.m_fDividerZ );
				deformNode->getAttr( "DividerW",m_shaderResources.m_DeformInfo.m_fDividerW );
				deformNode->getAttr( "NoiseScale",m_shaderResources.m_DeformInfo.m_vNoiseScale );

				XmlNodeRef waveX = deformNode->findChild("WaveX");
				if (waveX)
				{
					int type = eWF_None;
					waveX->getAttr( "Type",type );
					m_shaderResources.m_DeformInfo.m_WaveX.m_eWFType = (EWaveForm)type;
					waveX->getAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveX.m_Amp );
					waveX->getAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveX.m_Level );
					waveX->getAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveX.m_Phase );
					waveX->getAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveX.m_Freq );
				}
				XmlNodeRef waveY = deformNode->findChild("WaveY");
				if (waveY)
				{
					int type = eWF_None;
					waveY->getAttr( "Type",type );
					m_shaderResources.m_DeformInfo.m_WaveY.m_eWFType = (EWaveForm)type;
					waveY->getAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveY.m_Amp );
					waveY->getAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveY.m_Level );
					waveY->getAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveY.m_Phase );
					waveY->getAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveY.m_Freq );
				}
				XmlNodeRef waveZ = deformNode->findChild("WaveZ");
				if (waveZ)
				{
					int type = eWF_None;
					waveZ->getAttr( "Type",type );
					m_shaderResources.m_DeformInfo.m_WaveZ.m_eWFType = (EWaveForm)type;
					waveZ->getAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveZ.m_Amp );
					waveZ->getAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveZ.m_Level );
					waveZ->getAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveZ.m_Phase );
					waveZ->getAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveZ.m_Freq );
				}

				XmlNodeRef waveW = deformNode->findChild("WaveW");
				if (waveW)
				{
					int type = eWF_None;
					waveW->getAttr( "Type",type );
					m_shaderResources.m_DeformInfo.m_WaveW.m_eWFType = (EWaveForm)type;
					waveW->getAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveW.m_Amp );
					waveW->getAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveW.m_Level );
					waveW->getAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveW.m_Phase );
					waveW->getAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveW.m_Freq );
				}
			}
		}
		else
		{
			m_shaderResources.m_DeformInfo = pMtlTmpl->m_shaderResources.m_DeformInfo;
		}

		//////////////////////////////////////////////////////////////////////////
		// Check for detail decal
		//////////////////////////////////////////////////////////////////////////
		XmlNodeRef detailDecalNode = node->findChild("DetailDecal");
		if( detailDecalNode && (m_mtlFlags&MTL_FLAG_DETAIL_DECAL) )
		{
			SDetailDecalInfo *pDetailDecalInfo = &m_shaderResources.m_DetailDecalInfo;
			detailDecalNode->getAttr( "Opacity", pDetailDecalInfo->nBlending);
			detailDecalNode->getAttr( "SSAOAmount", pDetailDecalInfo->nSSAOAmount);      
			detailDecalNode->getAttr( "topTileU", pDetailDecalInfo->vTileOffs[0].x);
			detailDecalNode->getAttr( "topTileV", pDetailDecalInfo->vTileOffs[0].y);
			detailDecalNode->getAttr( "topOffsV", pDetailDecalInfo->vTileOffs[0].z);
			detailDecalNode->getAttr( "topOffsU", pDetailDecalInfo->vTileOffs[0].w);
			detailDecalNode->getAttr( "topRotation", pDetailDecalInfo->nRotation[0]);
			detailDecalNode->getAttr( "topDeformation", pDetailDecalInfo->nDeformation[0]);
			detailDecalNode->getAttr( "topSortOffset", pDetailDecalInfo->nThreshold[0]);
			detailDecalNode->getAttr( "bottomTileU", pDetailDecalInfo->vTileOffs[1].x);
			detailDecalNode->getAttr( "bottomTileV", pDetailDecalInfo->vTileOffs[1].y);
			detailDecalNode->getAttr( "bottomOffsV", pDetailDecalInfo->vTileOffs[1].z);
			detailDecalNode->getAttr( "bottomOffsU", pDetailDecalInfo->vTileOffs[1].w);
			detailDecalNode->getAttr( "bottomRotation", pDetailDecalInfo->nRotation[1]);
			detailDecalNode->getAttr( "bottomDeformation", pDetailDecalInfo->nDeformation[1]);
			detailDecalNode->getAttr( "bottomSortOffset", pDetailDecalInfo->nThreshold[1]);
		}
		else
			m_shaderResources.m_DetailDecalInfo.Reset();

		ClearAllSubMaterials();
		// Serialize sub materials.
		XmlNodeRef childsNode = node->findChild( "SubMaterials" );
		if (childsNode)
		{
			if (!ctx.bIgnoreChilds)
			{
				CString name;
				int nSubMtls = childsNode->getChildCount();
				SetSubMaterialCount(nSubMtls);
				for (int i = 0; i < nSubMtls; i++)
				{
					XmlNodeRef mtlNode = childsNode->getChild(i);
					if (mtlNode->isTag("Material"))
					{
						mtlNode->getAttr("Name",name);
						CMaterial *pSubMtl = new CMaterial(m_pManager,name,MTL_FLAG_PURE_CHILD);
						SetSubMaterial( i,pSubMtl );

						SerializeContext childCtx(ctx);
						childCtx.node = mtlNode;
						pSubMtl->Serialize( childCtx );

						pSubMtl->m_shaderResources.m_SortPrio = nSubMtls - i - 1;
					}
					else
					{
						if (mtlNode->getAttr("Name",name))
						{
							CMaterial *pMtl = m_pManager->LoadMaterial(name);
							if (pMtl)
								SetSubMaterial(i,pMtl);
						}
					}
				}
			}
		}

		//////////////////////////////////////////////////////////////////////////
		// Load public parameters.
		//////////////////////////////////////////////////////////////////////////
		if( !pMtlTmpl )
		{
			m_publicVarsCache = node->findChild( "PublicParams" );
		}
		else
		{
			m_publicVarsCache = node->newChild( "PublicParams" );
			if( pMtlTmpl->m_publicVarsCache )
			{
				m_publicVarsCache = pMtlTmpl->m_publicVarsCache->clone();
			}
			else
			{
				SetXmlFromShaderParams(pMtlTmpl->m_shaderResources, m_publicVarsCache );
			}
		}

		//////////////////////////////////////////////////////////////////////////
		// Load material layers data
		//////////////////////////////////////////////////////////////////////////
		if( pMtlTmpl )
		{
			for(int n=0;n<MTL_LAYER_MAX_SLOTS;n++)
			{
				m_pMtlLayerResources[n] = pMtlTmpl->m_pMtlLayerResources[n];
			}
		}
		else
		{
			XmlNodeRef mtlLayersNode = node->findChild( "MaterialLayers" ); 
			if(mtlLayersNode)
			{
				int nChildCount = min( (int) MTL_LAYER_MAX_SLOTS, (int)  mtlLayersNode->getChildCount() );
				for (int l = 0; l < nChildCount; ++l)
				{
					XmlNodeRef layerNode = mtlLayersNode->getChild(l);
					if(layerNode)
					{
						if( layerNode->getAttr( "Name", m_pMtlLayerResources[l].m_shaderName) )
						{
							m_pMtlLayerResources[l].m_bRegetPublicParams = true;

							bool bNoDraw = false;
							layerNode->getAttr( "NoDraw", bNoDraw);          

							m_pMtlLayerResources[l].m_publicVarsCache = layerNode->findChild( "PublicParams" );

							if ( bNoDraw )
								m_pMtlLayerResources[l].m_nFlags |= MTL_LAYER_USAGE_NODRAW;
							else
								m_pMtlLayerResources[l].m_nFlags &= ~MTL_LAYER_USAGE_NODRAW;
						}
					}
				}
			}
		}

		if (ctx.bUndo)
		{
			LoadShader();
			UpdateMatInfo();
		}

		m_bIgnoreNotifyChange = false;
		SetModified(false);

		// If copy pasting or undo send update event.
		if (ctx.bCopyPaste || ctx.bUndo)
			NotifyChanged();
	}
	else
	{
		// Saving.
		node->setAttr( "MtlFlags", m_mtlFlags|MTL_64BIT_SHADERGENMASK); 

		if (!IsMultiSubMaterial())
		{
			node->setAttr( "Shader",m_shaderName );
			node->setAttr( "GenMask",m_nShaderGenMask );      

			// store shader gen bit mask string
			m_pszShaderGenMask = GetIEditor()->GetRenderer()->EF_GetStringFromShaderGlobalMaskGen( (const char*)m_shaderName, m_nShaderGenMask ); 
			node->setAttr( "StringGenMask",m_pszShaderGenMask );

			node->setAttr( "SurfaceType",m_surfaceType );
			node->setAttr( "MatTemplate",m_matTemplate );

			SInputShaderResources &sr = m_shaderResources;
			//if (!m_shaderName.IsEmpty() && (stricmp(m_shaderName,"nodraw") != 0))
			{
				// Save ligthing data.
				if (defaultShaderResource.m_LMaterial.m_Diffuse != m_shaderResources.m_LMaterial.m_Diffuse)
					node->setAttr( "Diffuse",ToVec3(m_shaderResources.m_LMaterial.m_Diffuse) );
				if (defaultShaderResource.m_LMaterial.m_Specular != m_shaderResources.m_LMaterial.m_Specular)
					node->setAttr( "Specular",ToVec3(m_shaderResources.m_LMaterial.m_Specular) );
				if (defaultShaderResource.m_LMaterial.m_Emission != m_shaderResources.m_LMaterial.m_Emission)
					node->setAttr( "Emissive",ToVec3(m_shaderResources.m_LMaterial.m_Emission) );
				if (defaultShaderResource.m_LMaterial.m_SpecShininess != m_shaderResources.m_LMaterial.m_SpecShininess)
					node->setAttr( "Shininess",m_shaderResources.m_LMaterial.m_SpecShininess );

				if (defaultShaderResource.m_Opacity != sr.m_Opacity)
					node->setAttr( "Opacity",sr.m_Opacity );
				if (defaultShaderResource.m_AlphaRef != sr.m_AlphaRef)
					node->setAttr( "AlphaTest",sr.m_AlphaRef );
				if (defaultShaderResource.m_GlowAmount != sr.m_GlowAmount)
					node->setAttr( "GlowAmount",sr.m_GlowAmount);
				if (defaultShaderResource.m_FurAmount != sr.m_FurAmount)
					node->setAttr( "FurAmount",sr.m_FurAmount);
				if (defaultShaderResource.m_HeatAmount != sr.m_HeatAmount)
					node->setAttr( "HeatAmountScaled",sr.m_HeatAmount);
				if (defaultShaderResource.m_CloakAmount != sr.m_CloakAmount)
					node->setAttr( "CloakAmount",sr.m_CloakAmount);

				if (defaultShaderResource.m_DeformInfo.m_eType != sr.m_DeformInfo.m_eType)
					node->setAttr( "vertModifType",sr.m_DeformInfo.m_eType );

				// Save texturing data.
				XmlNodeRef texturesNode = node->newChild( "Textures" );
				for (int i = 0; i < sizeof(sUsedTextures)/sizeof(sUsedTextures[0]); i++)
				{
					int texId = sUsedTextures[i].texId;
					if (!sr.m_Textures[texId].m_Name.empty())
					{
						XmlNodeRef texNode = texturesNode->newChild( "Texture" );
						//			texNode->setAttr( "TexID",texId );
						texNode->setAttr( "Map",sUsedTextures[i].name );
						texNode->setAttr( "File",sr.m_Textures[texId].m_Name.c_str() );

						if (sr.m_Textures[texId].m_Filter != defaultShaderResource.m_Textures[texId].m_Filter)
							texNode->setAttr( "Filter",sr.m_Textures[texId].m_Filter );
						if (sr.m_Textures[texId].m_bUTile != defaultShaderResource.m_Textures[texId].m_bUTile)
							texNode->setAttr( "IsTileU",sr.m_Textures[texId].m_bUTile );
						if (sr.m_Textures[texId].m_bVTile != defaultShaderResource.m_Textures[texId].m_bVTile)
							texNode->setAttr( "IsTileV",sr.m_Textures[texId].m_bVTile );
						if (sr.m_Textures[texId].m_Sampler.m_eTexType != defaultShaderResource.m_Textures[texId].m_Sampler.m_eTexType)
							texNode->setAttr( "TexType",sr.m_Textures[texId].m_Sampler.m_eTexType );

						SEfTexModificator& texm = *sr.m_Textures[texId].AddModificator();

						if (memcmp(&texm,&defaultTexMod,sizeof(texm)) == 0)
							continue;

						XmlNodeRef modNode = texNode->newChild( "TexMod" );
						//////////////////////////////////////////////////////////////////////////
						// Save texture modificators Modificators
						//////////////////////////////////////////////////////////////////////////
						if (texm.m_Tiling[0] != defaultTexMod.m_Tiling[0])
							modNode->setAttr( "TileU",texm.m_Tiling[0] );
						if (texm.m_Tiling[1] != defaultTexMod.m_Tiling[1])
							modNode->setAttr( "TileV",texm.m_Tiling[1] );
						if (texm.m_Offs[0] != defaultTexMod.m_Offs[0])
							modNode->setAttr( "OffsetU",texm.m_Offs[0] );
						if (texm.m_Offs[1] != defaultTexMod.m_Offs[1])
							modNode->setAttr( "OffsetV",texm.m_Offs[1] );
						if (texm.m_Rot[0] != defaultTexMod.m_Rot[0])
							modNode->setAttr( "RotateU",Word2Degr(texm.m_Rot[0]) );
						if (texm.m_Rot[1] != defaultTexMod.m_Rot[1])
							modNode->setAttr( "RotateV",Word2Degr(texm.m_Rot[1]) );
						if (texm.m_Rot[2] != defaultTexMod.m_Rot[2])
							modNode->setAttr( "RotateW",Word2Degr(texm.m_Rot[2]) );
						if (texm.m_eUMoveType != defaultTexMod.m_eUMoveType)
							modNode->setAttr( "TexMod_UOscillatorType",texm.m_eUMoveType );
						if (texm.m_eVMoveType != defaultTexMod.m_eVMoveType)
							modNode->setAttr( "TexMod_VOscillatorType",texm.m_eVMoveType );
						if (texm.m_eRotType != defaultTexMod.m_eRotType)
							modNode->setAttr( "TexMod_RotateType",texm.m_eRotType );
						if (texm.m_eTGType != defaultTexMod.m_eTGType)
							modNode->setAttr( "TexMod_TexGenType",texm.m_eTGType );

						if (texm.m_RotOscRate[0] != defaultTexMod.m_RotOscRate[0])
							modNode->setAttr( "TexMod_URotateRate",Word2Degr(texm.m_RotOscRate[0]) );
						if (texm.m_RotOscPhase[0] != defaultTexMod.m_RotOscPhase[0])
							modNode->setAttr( "TexMod_URotatePhase",Word2Degr(texm.m_RotOscPhase[0]) );
						if (texm.m_RotOscAmplitude[0] != defaultTexMod.m_RotOscAmplitude[0])
							modNode->setAttr( "TexMod_URotateAmplitude",Word2Degr(texm.m_RotOscAmplitude[0]) );
						if (texm.m_RotOscRate[1] != defaultTexMod.m_RotOscRate[1])
							modNode->setAttr( "TexMod_VRotateRate",Word2Degr(texm.m_RotOscRate[1]) );
						if (texm.m_RotOscPhase[1] != defaultTexMod.m_RotOscPhase[1])
							modNode->setAttr( "TexMod_VRotatePhase",Word2Degr(texm.m_RotOscPhase[1]) );
						if (texm.m_RotOscAmplitude[1] != defaultTexMod.m_RotOscAmplitude[1])
							modNode->setAttr( "TexMod_VRotateAmplitude",Word2Degr(texm.m_RotOscAmplitude[1]) );
						if (texm.m_RotOscRate[2] != defaultTexMod.m_RotOscRate[2])
							modNode->setAttr( "TexMod_WRotateRate",Word2Degr(texm.m_RotOscRate[2]) );
						if (texm.m_RotOscPhase[2] != defaultTexMod.m_RotOscPhase[2])
							modNode->setAttr( "TexMod_WRotatePhase",Word2Degr(texm.m_RotOscPhase[2]) );
						if (texm.m_RotOscAmplitude[2] != defaultTexMod.m_RotOscAmplitude[2])
							modNode->setAttr( "TexMod_WRotateAmplitude",Word2Degr(texm.m_RotOscAmplitude[2]) );
						if (texm.m_RotOscCenter[0] != defaultTexMod.m_RotOscCenter[0])
							modNode->setAttr( "TexMod_URotateCenter",texm.m_RotOscCenter[0] );
						if (texm.m_RotOscCenter[1] != defaultTexMod.m_RotOscCenter[1])
							modNode->setAttr( "TexMod_VRotateCenter",texm.m_RotOscCenter[1] );
						if (texm.m_RotOscCenter[2] != defaultTexMod.m_RotOscCenter[2])
							modNode->setAttr( "TexMod_WRotateCenter",texm.m_RotOscCenter[2] );

						if (texm.m_UOscRate != defaultTexMod.m_UOscRate)
							modNode->setAttr( "TexMod_UOscillatorRate",texm.m_UOscRate );
						if (texm.m_VOscRate != defaultTexMod.m_VOscRate)
							modNode->setAttr( "TexMod_VOscillatorRate",texm.m_VOscRate );
						if (texm.m_UOscPhase != defaultTexMod.m_UOscPhase)
							modNode->setAttr( "TexMod_UOscillatorPhase",texm.m_UOscPhase );
						if (texm.m_VOscPhase != defaultTexMod.m_VOscPhase)
							modNode->setAttr( "TexMod_VOscillatorPhase",texm.m_VOscPhase );
						if (texm.m_UOscAmplitude != defaultTexMod.m_UOscAmplitude)
							modNode->setAttr( "TexMod_UOscillatorAmplitude",texm.m_UOscAmplitude );
						if (texm.m_VOscAmplitude != defaultTexMod.m_VOscAmplitude)
							modNode->setAttr( "TexMod_VOscillatorAmplitude",texm.m_VOscAmplitude );
					}
				}
			}
		}

		//////////////////////////////////////////////////////////////////////////
		// Check if we have vertex deform.
		//////////////////////////////////////////////////////////////////////////
		if (m_shaderResources.m_DeformInfo.m_eType != eDT_Unknown)
		{
			XmlNodeRef deformNode = node->newChild("VertexDeform");
			deformNode->setAttr( "Type",m_shaderResources.m_DeformInfo.m_eType );
			deformNode->setAttr( "DividerX",m_shaderResources.m_DeformInfo.m_fDividerX );
			deformNode->setAttr( "DividerY",m_shaderResources.m_DeformInfo.m_fDividerY );
			deformNode->setAttr( "DividerZ",m_shaderResources.m_DeformInfo.m_fDividerZ );
			deformNode->setAttr( "DividerW",m_shaderResources.m_DeformInfo.m_fDividerW );
			deformNode->setAttr( "NoiseScale",m_shaderResources.m_DeformInfo.m_vNoiseScale );

			if (m_shaderResources.m_DeformInfo.m_WaveX.m_eWFType != eWF_None)
			{
				XmlNodeRef waveX = deformNode->newChild("WaveX");
				waveX->setAttr( "Type",m_shaderResources.m_DeformInfo.m_WaveX.m_eWFType );
				waveX->setAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveX.m_Amp );
				waveX->setAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveX.m_Level );
				waveX->setAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveX.m_Phase );
				waveX->setAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveX.m_Freq );
			}
			if (m_shaderResources.m_DeformInfo.m_WaveY.m_eWFType != eWF_None)
			{
				XmlNodeRef waveY = deformNode->newChild("WaveY");
				waveY->setAttr( "Type",m_shaderResources.m_DeformInfo.m_WaveY.m_eWFType );
				waveY->setAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveY.m_Amp );
				waveY->setAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveY.m_Level );
				waveY->setAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveY.m_Phase );
				waveY->setAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveY.m_Freq );
			}
			if (m_shaderResources.m_DeformInfo.m_WaveZ.m_eWFType != eWF_None)
			{
				XmlNodeRef waveZ = deformNode->newChild("WaveZ");
				waveZ->setAttr( "Type",m_shaderResources.m_DeformInfo.m_WaveZ.m_eWFType );
				waveZ->setAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveZ.m_Amp );
				waveZ->setAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveZ.m_Level );
				waveZ->setAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveZ.m_Phase );
				waveZ->setAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveZ.m_Freq );
			}
			if (m_shaderResources.m_DeformInfo.m_WaveW.m_eWFType != eWF_None)
			{
				XmlNodeRef waveW = deformNode->newChild("WaveW");
				waveW->setAttr( "Type",m_shaderResources.m_DeformInfo.m_WaveW.m_eWFType );
				waveW->setAttr( "Amp",m_shaderResources.m_DeformInfo.m_WaveW.m_Amp );
				waveW->setAttr( "Level",m_shaderResources.m_DeformInfo.m_WaveW.m_Level );
				waveW->setAttr( "Phase",m_shaderResources.m_DeformInfo.m_WaveW.m_Phase );
				waveW->setAttr( "Freq",m_shaderResources.m_DeformInfo.m_WaveW.m_Freq );
			}
		}

		//////////////////////////////////////////////////////////////////////////
		// Check for detail decal
		//////////////////////////////////////////////////////////////////////////
		if( m_mtlFlags&MTL_FLAG_DETAIL_DECAL)
		{
			XmlNodeRef detailDecalNode = node->newChild( "DetailDecal" );
			SDetailDecalInfo *pDetailDecalInfo = &m_shaderResources.m_DetailDecalInfo;
			detailDecalNode->setAttr( "Opacity", pDetailDecalInfo->nBlending);
			detailDecalNode->setAttr( "SSAOAmount", pDetailDecalInfo->nSSAOAmount);      
			detailDecalNode->setAttr( "topTileU", pDetailDecalInfo->vTileOffs[0].x);
			detailDecalNode->setAttr( "topTileV", pDetailDecalInfo->vTileOffs[0].y);
			detailDecalNode->setAttr( "topOffsV", pDetailDecalInfo->vTileOffs[0].z);
			detailDecalNode->setAttr( "topOffsU", pDetailDecalInfo->vTileOffs[0].w);
			detailDecalNode->setAttr( "topRotation", pDetailDecalInfo->nRotation[0]);
			detailDecalNode->setAttr( "topDeformation", pDetailDecalInfo->nDeformation[0]);      
			detailDecalNode->setAttr( "topSortOffset", pDetailDecalInfo->nThreshold[0]);
			detailDecalNode->setAttr( "bottomTileU", pDetailDecalInfo->vTileOffs[1].x);
			detailDecalNode->setAttr( "bottomTileV", pDetailDecalInfo->vTileOffs[1].y);
			detailDecalNode->setAttr( "bottomOffsV", pDetailDecalInfo->vTileOffs[1].z);
			detailDecalNode->setAttr( "bottomOffsU", pDetailDecalInfo->vTileOffs[1].w);
			detailDecalNode->setAttr( "bottomRotation", pDetailDecalInfo->nRotation[1]);
			detailDecalNode->setAttr( "bottomDeformation", pDetailDecalInfo->nDeformation[1]);      
			detailDecalNode->setAttr( "bottomSortOffset", pDetailDecalInfo->nThreshold[1]);
		}

		if (GetSubMaterialCount() > 0)
		{
			// Serialize sub materials.
			XmlNodeRef childsNode = node->newChild( "SubMaterials" );
			for (int i = 0; i < GetSubMaterialCount(); i++)
			{
				CMaterial *pSubMtl = GetSubMaterial(i);
				if (pSubMtl && pSubMtl->IsPureChild())
				{
					XmlNodeRef mtlNode = childsNode->newChild( "Material" );
					mtlNode->setAttr( "Name",pSubMtl->GetName() );
					SerializeContext childCtx(ctx);
					childCtx.node = mtlNode;
					GetSubMaterial(i)->Serialize( childCtx );
				}
				else
				{
					XmlNodeRef mtlNode = childsNode->newChild( "MaterialRef" );
					if (pSubMtl)
						mtlNode->setAttr( "Name",pSubMtl->GetName() );
				}
			}
		}

		//////////////////////////////////////////////////////////////////////////
		// Save public parameters.
		//////////////////////////////////////////////////////////////////////////
		if (m_publicVarsCache)
		{
			node->addChild( m_publicVarsCache );
		}
		else if (!m_shaderResources.m_ShaderParams.empty())
		{
			XmlNodeRef publicsNode = node->newChild( "PublicParams" );
			SetXmlFromShaderParams( m_shaderResources, publicsNode );
		}

		//////////////////////////////////////////////////////////////////////////
		// Save material layers data
		//////////////////////////////////////////////////////////////////////////

		bool bMaterialLayers = false;
		for(int l(0); l < MTL_LAYER_MAX_SLOTS; ++l)
		{
			if(!m_pMtlLayerResources[l].m_shaderName.IsEmpty())
			{
				bMaterialLayers = true;
				break;
			}
		}

		if( bMaterialLayers )
		{
			XmlNodeRef mtlLayersNode = node->newChild( "MaterialLayers" );    
			for(int l(0); l < MTL_LAYER_MAX_SLOTS; ++l)
			{
				XmlNodeRef layerNode = mtlLayersNode->newChild( "Layer" );
				if(!m_pMtlLayerResources[l].m_shaderName.IsEmpty())
				{
					layerNode->setAttr( "Name", m_pMtlLayerResources[l].m_shaderName);                        
					layerNode->setAttr( "NoDraw", m_pMtlLayerResources[l].m_nFlags & MTL_LAYER_USAGE_NODRAW );     

					if(m_pMtlLayerResources[l].m_publicVarsCache)
					{
						layerNode->addChild( m_pMtlLayerResources[l].m_publicVarsCache );
					}
					else if( !m_pMtlLayerResources[l].m_shaderResources.m_ShaderParams.empty() ) 
					{
						XmlNodeRef publicsNode = layerNode->newChild( "PublicParams" );
						SetXmlFromShaderParams( m_pMtlLayerResources[l].m_shaderResources, publicsNode ); 
					}
				}
			}
		}
	}
}

/*
//////////////////////////////////////////////////////////////////////////
void CMaterial::SerializePublics( XmlNodeRef &node,bool bLoading )
{
	if (bLoading)
	{
	}
	else
	{
		if (m_shaderParams.empty())
			return;
		XmlNodeRef publicsNode = node->newChild( "PublicParams" );

		for (int i = 0; i < m_shaderParams.size(); i++)
		{
			XmlNodeRef paramNode = node->newChild( "Param" );
			SShaderParam *pParam = &m_shaderParams[i];
			paramNode->setAttr( "Name",pParam->m_Name );
			switch (pParam->m_Type)
			{
			case eType_BYTE:
				paramNode->setAttr( "Value",(int)pParam->m_Value.m_Byte );
				paramNode->setAttr( "Type",(int)pParam->m_Value.m_Byte );
				break;
			case eType_SHORT:
				paramNode->setAttr( "Value",(int)pParam->m_Value.m_Short );
				break;
			case eType_INT:
				paramNode->setAttr( "Value",(int)pParam->m_Value.m_Int );
				break;
			case eType_FLOAT:
				paramNode->setAttr( "Value",pParam->m_Value.m_Float );
				break;
			case eType_STRING:
				paramNode->setAttr( "Value",pParam->m_Value.m_String );
			break;
			case eType_FCOLOR:
				paramNode->setAttr( "Value",Vec3(pParam->m_Value.m_Color[0],pParam->m_Value.m_Color[1],pParam->m_Value.m_Color[2]) );
				break;
			case eType_VECTOR:
				paramNode->setAttr( "Value",Vec3(pParam->m_Value.m_Vector[0],pParam->m_Value.m_Vector[1],pParam->m_Value.m_Vector[2]) );
				break;
			}
		}
	}
}
*/

//////////////////////////////////////////////////////////////////////////
void CMaterial::AssignToEntity( IRenderNode *pEntity )
{
	assert( pEntity );
	pEntity->SetMaterial( GetMatInfo() );
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetFromMatInfo( IMaterial *pMatInfo )
{
	assert( pMatInfo );

	m_bFromEngine = true;
	m_shaderName = "";
	
	ClearMatInfo();
	SetModified(true);

	m_mtlFlags = pMatInfo->GetFlags();
	if (m_mtlFlags & MTL_FLAG_MULTI_SUBMTL)
	{
		// Create sub materials.
		SetSubMaterialCount( pMatInfo->GetSubMtlCount() );
		for (int i = 0; i < GetSubMaterialCount(); i++)
		{
			IMaterial *pChildMatInfo = pMatInfo->GetSubMtl(i);
			if (!pChildMatInfo)
				continue;

			if (pChildMatInfo->GetFlags() & MTL_FLAG_PURE_CHILD)
			{
				CMaterial *pChild = new CMaterial( GetMatManager(),pChildMatInfo->GetName(),pChildMatInfo->GetFlags() );
				pChild->SetFromMatInfo( pChildMatInfo );
				SetSubMaterial(i,pChild);
			}
			else
			{
				CMaterial *pChild = GetMatManager()->LoadMaterial( pChildMatInfo->GetName() );
				pChild->SetFromMatInfo( pChildMatInfo );
				SetSubMaterial(i,pChild);
			}
		}
	}
	else
	{
		SAFE_RELEASE( m_shaderItem.m_pShader );
		SAFE_RELEASE( m_shaderItem.m_pShaderResources );
		m_shaderItem = pMatInfo->GetShaderItem();
		if (m_shaderItem.m_pShader)
			m_shaderItem.m_pShader->AddRef();
		if (m_shaderItem.m_pShaderResources)
			m_shaderItem.m_pShaderResources->AddRef();

		if (m_shaderItem.m_pShaderResources)
		{
			m_shaderResources = SInputShaderResources(m_shaderItem.m_pShaderResources);
		}
		if (m_shaderItem.m_pShader)
		{
			// Get name of template.
			IShader *pTemplShader = m_shaderItem.m_pShader;
			if (pTemplShader)
			{
				m_shaderName = pTemplShader->GetName();
				m_nShaderGenMask = pTemplShader->GetGenerationMask();
			}
		}
		ISurfaceType *pSurfaceType = pMatInfo->GetSurfaceType();
		if (pSurfaceType)
			m_surfaceType = pSurfaceType->GetName();
		else
			m_surfaceType = "";
    //
    IMaterial *pMatTemplate = pMatInfo->GetMatTemplate();
    if (pMatTemplate)
      m_matTemplate = pMatTemplate->GetName();
    else
      m_matTemplate = "";
	}

	// Mark as not modified.
	SetModified(false);

	//////////////////////////////////////////////////////////////////////////
	// Assign mat info.
	m_pMatInfo = pMatInfo;
	m_pMatInfo->SetUserData( this );
	AddRef(); // Let IMaterial keep a reference to us.
}

//////////////////////////////////////////////////////////////////////////
int CMaterial::GetSubMaterialCount() const
{
	return m_subMaterials.size();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetSubMaterialCount( int nSubMtlsCount )
{
	RecordUndo("Multi Material Change");
	m_subMaterials.resize(nSubMtlsCount);
	UpdateMatInfo();
	NotifyChanged();
}
	
//////////////////////////////////////////////////////////////////////////
CMaterial* CMaterial::GetSubMaterial( int index ) const
{
	assert( index >= 0 && index < m_subMaterials.size() );
	return m_subMaterials[index];
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetSubMaterial( int nSlot,CMaterial *mtl )
{
	RecordUndo("Multi Material Change");
	assert( nSlot >= 0 && nSlot < m_subMaterials.size() );
	if (mtl)
	{
		if (mtl->m_bFromEngine && !m_bFromEngine)
		{
			// Do not allow to assign engine materials to sub-slots of real materials.
			return;
		}
		if (mtl->IsMultiSubMaterial())
			return;
		if (mtl->IsPureChild())
			mtl->m_pParent = this;
	}

	if (m_subMaterials[nSlot])
		m_subMaterials[nSlot]->m_pParent = NULL;
	m_subMaterials[nSlot] = mtl;
	
	UpdateMatInfo();
	NotifyChanged();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::ClearAllSubMaterials()
{
	RecordUndo("Multi Material Change");
	for (int i = 0; i < m_subMaterials.size(); i++)
	{
		if (m_subMaterials[i])
		{
			m_subMaterials[i]->m_pParent = NULL;
			m_subMaterials[i] = NULL;
		}
	}
	UpdateMatInfo();
	NotifyChanged();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::Validate()
{
	if (IsDummy())
	{
		CErrorRecord err;
		err.error.Format( _T("Material %s file not found"),(const char*)GetName() );
		err.pItem = this;
		GetIEditor()->GetErrorReport()->ReportError( err );
	}
	// Reload shader.
	LoadShader();

	// Validate sub materials.
	for (int i = 0; i < m_subMaterials.size(); i++)
	{
		if (m_subMaterials[i])
			m_subMaterials[i]->Validate();
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::GatherUsedResources( CUsedResources &resources )
{
	if (!IsUsed())
		return;

	SInputShaderResources &sr = GetShaderResources();
	for (int texid = 0; texid < EFTT_MAX; texid++)
	{
		if (!sr.m_Textures[texid].m_Name.empty())
		{
			resources.Add( sr.m_Textures[texid].m_Name.c_str() );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CMaterial::CanModify( bool bSkipReadOnly )
{
	if (m_bDummyMaterial)
		return false;

	if (m_bFromEngine)
		return false;

	if (IsPureChild() && GetParent())
		return GetParent()->CanModify(bSkipReadOnly);

	if (bSkipReadOnly)
	{
		// If read only or in pack, do not save.
		if (m_scFileAttributes&(SCC_FILE_ATTRIBUTE_READONLY|SCC_FILE_ATTRIBUTE_INPAK))
			return false;

		// Managed file must be checked out.
		if ((m_scFileAttributes&SCC_FILE_ATTRIBUTE_MANAGED) && !(m_scFileAttributes&SCC_FILE_ATTRIBUTE_CHECKEDOUT))
			return false;
	}
	else
	{
		// Only if in pack.
		if (m_scFileAttributes&(SCC_FILE_ATTRIBUTE_INPAK))
			return false;
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CMaterial::Save( bool bSkipReadOnly )
{
	if (!CanModify( bSkipReadOnly ))
		return false;

	// Save our parent
	if (IsPureChild())
	{
		if (m_pParent)
			return m_pParent->Save(bSkipReadOnly);
		return false;
	}

	// If filename is empty do not not save.
	if (GetFilename(true).IsEmpty())
		return false;

	// Save material XML to a file that corresponds to the material name with extension .mtl.
	XmlNodeRef mtlNode = XmlHelpers::CreateXmlNode( "Material" );
	CBaseLibraryItem::SerializeContext ctx( mtlNode,false );
	Serialize( ctx );

	//CMaterialManager *pMatMan = (CMaterialManager*)GetLibrary()->GetManager();
	// get file name from material name.
	//CString filename = pMatMan->MaterialToFilename( GetName() );

	//char path[ICryPak::g_nMaxPath];
	//filename = gEnv->pCryPak->AdjustFileName( filename,path,0 );

	if (XmlHelpers::SaveXmlNode( mtlNode,GetFilename(true)))
	{
		// If material successfully saved, clear modified flag.
		SetModified(false);
		for (int i=0; i<GetSubMaterialCount(); ++i)
		{
			CMaterial* pSubMaterial = GetSubMaterial(i);
			if (pSubMaterial)
				pSubMaterial->SetModified(false);
		}
		return true;
	}
	else
		return false;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::ClearMatInfo()
{
	IMaterial *pMatInfo = m_pMatInfo;
	m_pMatInfo = 0;
	// This call can release CMaterial.
	if (pMatInfo)
		Release();
}

//////////////////////////////////////////////////////////////////////////
IMaterial* CMaterial::GetMatInfo(bool bUseExistingEngineMaterial)
{
	if (!m_pMatInfo)
	{
    
		if (m_bDummyMaterial)
		{
			m_pMatInfo = GetIEditor()->Get3DEngine()->GetMaterialManager()->GetDefaultMaterial();
			AddRef(); // Always keep dummy materials.
			return m_pMatInfo;
		}

		if (!IsMultiSubMaterial() && !m_shaderItem.m_pShader)
		{
			LoadShader();
		}

    if (!IsPureChild()) {
      if (bUseExistingEngineMaterial)
        m_pMatInfo = GetIEditor()->Get3DEngine()->GetMaterialManager()->FindMaterial( GetName() );

      if (!m_pMatInfo)
        m_pMatInfo = GetIEditor()->Get3DEngine()->GetMaterialManager()->CreateMaterial( GetName(),m_mtlFlags );
    }
    else
		{
			// Pure child should not be registered with the name.
			m_pMatInfo = GetIEditor()->Get3DEngine()->GetMaterialManager()->CreateMaterial( "",m_mtlFlags );
			m_pMatInfo->SetName( GetName() );
		}
		m_mtlFlags = m_pMatInfo->GetFlags();
		UpdateMatInfo();

    if (m_pMatInfo->GetUserData() != this) {
      m_pMatInfo->SetUserData( this );
		  AddRef(); // Let IMaterial keep a reference to us.
    }
	}
	return m_pMatInfo;
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::NotifyChanged()
{
	if (m_bIgnoreNotifyChange)
		return;

	if (!CanModify() && !IsModified() && CUndo::IsRecording())
	{
		// Display Warning message.
		Warning( "Modifying read only material %s\r\nChanges will not be saved!",(const char*)GetName() );
	}

	SetModified();

	m_pManager->OnItemChanged(this);

	SyncMaterialToConsole();
} 

//////////////////////////////////////////////////////////////////////////
void CMaterial::SyncMaterialToConsole()
{
	if (!IsPureChild())
	{
		GetIEditor()->GetConsoleSync()->SyncMaterial( this );
	}
	else if (m_pParent)
	{
		GetIEditor()->GetConsoleSync()->SyncMaterial( m_pParent );
	}
}

//////////////////////////////////////////////////////////////////////////
class CUndoMaterial : public IUndoObject
{
public:
	CUndoMaterial( CMaterial *pMaterial, const char *undoDescription, bool bForceUpdate )
	{
		// Stores the current state of this object.
		m_undoDescription = undoDescription;
		m_mtlName = pMaterial->GetName();
		// Save material XML to a file that corresponds to the material name with extension .mtl.
		m_undo = XmlHelpers::CreateXmlNode( "Material" );
		CBaseLibraryItem::SerializeContext ctx( m_undo,false );
		pMaterial->Serialize( ctx );
		m_bForceUpdate = bForceUpdate;
	}
protected:
	virtual void Release() { delete this; };
	virtual int GetSize()
	{
		return sizeof(*this) + m_undoDescription.GetLength();
	}
	virtual const char* GetDescription() { return m_undoDescription; };

	virtual void Undo( bool bUndo )
	{
		CMaterial* pMaterial = (CMaterial*)GetIEditor()->GetMaterialManager()->FindItemByName(m_mtlName);
		if (!pMaterial)
			return;

		if (bUndo)
		{
			// Save current object state.
			m_redo = XmlHelpers::CreateXmlNode( "Material" );
			CBaseLibraryItem::SerializeContext ctx( m_redo,false );
			pMaterial->Serialize( ctx );
		}
		CBaseLibraryItem::SerializeContext ctx( m_undo,true );
		ctx.bUndo = true;
		pMaterial->Serialize( ctx );
		if(m_bForceUpdate && bUndo)
			GetIEditor()->GetMaterialManager()->OnUpdateProperties(pMaterial);
	}
	virtual void Redo()
	{
		CMaterial* pMaterial = (CMaterial*)GetIEditor()->GetMaterialManager()->FindItemByName(m_mtlName);
		if (!pMaterial)
			return;

		CBaseLibraryItem::SerializeContext ctx( m_redo,true );
		ctx.bUndo = true;
		pMaterial->Serialize( ctx );
		if(m_bForceUpdate)
			GetIEditor()->GetMaterialManager()->OnUpdateProperties(pMaterial);
	}

private:
	CString m_undoDescription;
	CString m_mtlName;
	XmlNodeRef m_undo;
	XmlNodeRef m_redo;
	bool m_bForceUpdate;
};

//////////////////////////////////////////////////////////////////////////
void CMaterial::RecordUndo( const char *sText, bool bForceUpdate )
{
	if (CUndo::IsRecording())
	{
		CUndo::Record( new CUndoMaterial(this, sText, bForceUpdate) );
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::OnMakeCurrent()
{
	UpdateFileAttributes();
	
	// If Shader not yet loaded, load it now.
	if (!m_shaderItem.m_pShader)
		LoadShader();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetSurfaceTypeName( const CString &surfaceType )
{
	m_surfaceType = surfaceType;
	UpdateMatInfo();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetMatTemplate( const CString &matTemplate )
{
  m_matTemplate = matTemplate;
  //UpdateMatInfo();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::Reload()
{
	if (IsPureChild())
	{
		if (m_pParent)
			m_pParent->Reload();
		return;
	}
	if (IsDummy())
		return;

	XmlNodeRef mtlNode = GetISystem()->LoadXmlFromFile( GetFilename() );
	if (!mtlNode)
	{
		return;
	}
	CBaseLibraryItem::SerializeContext serCtx( mtlNode,true );
	serCtx.bUndo = true; // Simulate undo.
	Serialize( serCtx );

	// was called by Simulate undo.
	//UpdateMatInfo();
	//NotifyChanged();
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::SetHighlighted( bool bHighlight )
{
	if (!m_pHighlightData && bHighlight)
	{
		IMaterial* pMatInfo = GetMatInfo();
		if(pMatInfo)
			m_pHighlightData = new CMaterialHighlightData(pMatInfo);
	}
	else if (m_pHighlightData && !bHighlight)
	{
		delete m_pHighlightData;
		m_pHighlightData = 0;
	}
}

//////////////////////////////////////////////////////////////////////////
void CMaterial::DisableHighlightForFrame()
{
	if(m_pHighlightData)
		m_pHighlightData->Restore();
}




//////////////////////////////////////////////////////////////////////////
// CMaterialHighlightData
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CMaterialHighlightData::CMaterialHighlightData( IMaterial* pMaterial ) : 
	m_pMaterial( 0 )
{
	SetMaterial(pMaterial);
}

//////////////////////////////////////////////////////////////////////////
void CMaterialHighlightData::SetMaterial( IMaterial* pMaterial )
{
	if(m_pMaterial == pMaterial)
		return;

	if(m_pMaterial)
	{
		GetIEditor()->UnregisterNotifyListener(this);
		Restore();
	}

	m_pMaterial = pMaterial;
	if (m_pMaterial) 
	{
		if(!(m_pMaterial->GetFlags() & MTL_FLAG_NODRAW))
		{
			std::vector<IMaterial*> materialList;
			if( GetMaterialList(m_pMaterial, materialList) )
			{
				m_prevLightMaterials.clear();
				for( size_t i = 0; i < materialList.size(); i++ )
				{
					const SShaderItem &shaderItem = materialList[i]->GetShaderItem();
					if (shaderItem.m_pShaderResources)
					{
						CInputLightMaterial ilm;
						ColorF & dCol = shaderItem.m_pShaderResources->GetDiffuseColor();
						ColorF & eCol = shaderItem.m_pShaderResources->GetEmissiveColor();
						ilm.m_Diffuse = dCol;
						ilm.m_Emission = eCol;	
						m_prevLightMaterials.push_back(ilm);
					}
				}
			}
			GetIEditor()->RegisterNotifyListener(this);
		}
	}
}


//////////////////////////////////////////////////////////////////////////
CMaterialHighlightData::~CMaterialHighlightData()
{
	SetMaterial(0);
}


//////////////////////////////////////////////////////////////////////////
void CMaterialHighlightData::Restore()
{
	if (m_pMaterial && (!(m_pMaterial->GetFlags() & MTL_FLAG_NODRAW)))
	{
		std::vector<IMaterial*> materialList;

		if( GetMaterialList(m_pMaterial, materialList) == false )
			return;

		int counter = 0;
		for( size_t i = 0; i < materialList.size(); ++i )
		{
			const SShaderItem &shaderItem = materialList[i]->GetShaderItem();
			if(!shaderItem.m_pShaderResources)
				continue;
 			ColorF & dCol = shaderItem.m_pShaderResources->GetDiffuseColor();
			ColorF & eCol = shaderItem.m_pShaderResources->GetEmissiveColor();
 			dCol = m_prevLightMaterials[counter].m_Diffuse;
			eCol = m_prevLightMaterials[counter].m_Emission;			
 			shaderItem.m_pShaderResources->UpdateConstants(shaderItem.m_pShader);
			++counter;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CMaterialHighlightData::GetMaterialList( IMaterial* pMaterial, std::vector<IMaterial*>& outMaterialList ) const
{	
	if( pMaterial->GetSubMtlCount() > 0 )
	{
		for( int i = 0; i < pMaterial->GetSubMtlCount(); ++i )
		{
			IMaterial* pSubMaterial = pMaterial->GetSubMtl(i);	
			if( pSubMaterial )
				outMaterialList.push_back(pSubMaterial);
		}
	}
	else
	{
		outMaterialList.push_back(pMaterial);
	}
	return !outMaterialList.empty();
}


//////////////////////////////////////////////////////////////////////////
void CMaterialHighlightData::OnEditorNotifyEvent( EEditorNotifyEvent event )
{
	switch (event)
	{
	case eNotify_OnIdleUpdate:
		IdleUpdate();
		break;
	}
}


//////////////////////////////////////////////////////////////////////////
void CMaterialHighlightData::IdleUpdate()
{
	if( m_pMaterial->GetFlags() & MTL_FLAG_NODRAW )
		return;

	if (m_pMaterial)
	{
		std::vector<IMaterial*> materialList;
		if( GetMaterialList(m_pMaterial, materialList) == false )
			return;

		float t = GetTickCount() / 1000.0f;
		float r1 = fabs(sin(t*8.0f));
		if (r1 > 255)
			r1 = 255;
		COLORREF col = RGB( 255,0,r1*255 );

		int counter = 0;
		for( size_t i = 0; i < materialList.size(); ++i )
		{
			const SShaderItem &shaderItem = materialList[i]->GetShaderItem();
			if(!shaderItem.m_pShaderResources)
				continue;
				
			CInputLightMaterial lm = m_prevLightMaterials[counter];

			lm.m_Diffuse.r = GetRValue(col)/255.0f + 0.1f;
			lm.m_Diffuse.g = GetGValue(col)/255.0f + 0.1f;
			lm.m_Diffuse.b = GetBValue(col)/255.0f + 0.1f;
			lm.m_Emission.r = GetRValue(col)/255.0f;
			lm.m_Emission.g = GetGValue(col)/255.0f;
			lm.m_Emission.b = GetBValue(col)/255.0f;
			lm.m_Emission.a = 1.0f;

			ColorF & dCol = shaderItem.m_pShaderResources->GetDiffuseColor();
			ColorF & eCol = shaderItem.m_pShaderResources->GetEmissiveColor();
			
			dCol = lm.m_Diffuse;
			eCol = lm.m_Emission;
			shaderItem.m_pShaderResources->UpdateConstants(shaderItem.m_pShader);

			++counter;
		}
	}
}
